package net.lastcoder.ifserver.session;

import net.lastcoder.ifserver.Constants;
import net.lastcoder.ifserver.Server;
import net.lastcoder.ifserver.auth.AuthManager;
import net.lastcoder.ifserver.module.BaseModule;
import net.lastcoder.ifserver.module.ModuleManager;
import net.lastcoder.ifserver.module.ModuleNames;
import net.lastcoder.ifserver.nio.Connection;
import net.lastcoder.ifserver.nio.ConnectionCloseListener;
import net.lastcoder.ifserver.packet.Packet;
import net.lastcoder.ifserver.packet.SessionClosePacket;
import net.lastcoder.ifserver.packet.SessionOpenPacket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**一般有连接进来，在发送了会话包后，都会递交到这里处理
 * auth: WenYF
 * date: 2016/10/22
 */
public class SessionHandler extends BaseModule {
    private static final Logger Log = LoggerFactory.getLogger(SessionHandler.class);
    private SessionManager sessionManager;
    private AuthManager authManager;

    private ConnectionCloseListener connectionCloseListener = new ConnectionCloseListener() {
        @Override
        public void onConnectionClose(Object handback) {
            if (handback == null) {
                // 还没有建立会话就关闭了连接
                return;
            }
            ClientSession session = (ClientSession) handback;

            sessionManager.removeSession(session.getUsername());

            // 通知会话已经关闭，因为这个监听只能在会话建立的时候注册
            // 这里通知会话已经关闭
            for (SessionListener listener : sessionManager.getSessionListeners()) {
                listener.sessionDestroy(session);
            }
        }
    };

    public SessionHandler() {
        super(ModuleNames.SessionHandler);
    }

    private String serverName;

    @Override
    public void init() {
        sessionManager = (SessionManager) ModuleManager.getInstance()
                .getModule(ModuleNames.SessionManager);
        authManager = (AuthManager) ModuleManager.getInstance()
                .getModule(ModuleNames.AuthManager);
        serverName = Server.getInstance().getServerName();
    }

    @Override
    public void reload() {
    }

    @Override
    public void destroy() {
    }

    public void handle(Connection conn, Packet p) throws Exception {
        int type = p.getPacketType();
        if (type == Constants.PACKET_TYPE_SESSION_OPEN) {
            SessionOpenPacket packet = (SessionOpenPacket) p;
            if (packet.getType() != Packet.TYPE_SET) {
                //  TODO  收到的一个会话请求不是set，未知，丢弃
                return;
            }
            // 会话打开
            ClientSession session = (ClientSession) sessionManager.getSession(packet.getFrom());

            if (session != null) {
                if (session.getConnection() == conn) {
                    Log.info("the session has login, but receive a open session packet.");
                    SessionOpenPacket retSessionPacket = new SessionOpenPacket(Packet.TYPE_RESULT);
                    retSessionPacket.setFromAddress(packet.getToAddress());
                    retSessionPacket.setToAddress(packet.getFromAddress());
                    retSessionPacket.setExtra("login success".getBytes());

                    session.process(retSessionPacket);
                    return;
                }

                session.incrementConflictCount();
                Log.debug(session.getUsername() + " session conflict");
                SessionClosePacket conflictPacket = new SessionClosePacket(Packet.TYPE_SET);
                conflictPacket.setFromAddress(UlaID.fromAddress(Server.getInstance().getServerDomain()
                        + "/" + UlaID.RESOURCE_SERVER));
                conflictPacket.setId(Packet.generateId());
                conflictPacket.setExtra(Constants.CONFLICT_DESC.getBytes());
                session.process(conflictPacket);
                session.close();
            }
            session = ClientSession.createSession(conn, packet.getFromAddress());
            sessionManager.addSession(session);
            conn.init(session);

            // 通知会话建立
            for (SessionListener listener : sessionManager.getSessionListeners()) {
                listener.sessionCreated(session);
            }

            // 注册连接监听，只有在会话建立了才注册
            conn.registerCloseListener(connectionCloseListener, 0);

            // 打开会话的授权失败
            String token = new String(packet.getExtra(), Constants.DEFAUT_CAHRSET);
            session.setAttribute("token", token);
            if (!authManager.authorization(packet.getFrom(), token)) {
                SessionOpenPacket retSessionPacket = new SessionOpenPacket(Packet.TYPE_ERROR);
                retSessionPacket.setFromAddress(packet.getToAddress());
                retSessionPacket.setToAddress(packet.getFromAddress());
                retSessionPacket.setExtra(Constants.AUTH_ERROR.getBytes());

                conn.deliver(retSessionPacket);
                return;
            }

            // 置为已经授权
            session.setStatus(Session.STATUS_AUTHENTICATED);

            // 返回成功
            SessionOpenPacket retSessionPacket = new SessionOpenPacket(Packet.TYPE_RESULT);
            retSessionPacket.setFromAddress(packet.getToAddress());
            retSessionPacket.setToAddress(packet.getFromAddress());
            retSessionPacket.setExtra("login success".getBytes());

            session.process(retSessionPacket);

            // 通知会话建立
            for (SessionListener listener : sessionManager.getSessionListeners()) {
                listener.sessionAuthenticated(session);
            }
        } else if (type == Constants.PACKET_TYPE_SESSION_CLOSE) {
            SessionClosePacket packet = (SessionClosePacket) p;
            if (packet.getType() != Packet.TYPE_SET) {
                // TODO 收到的一个会话请求不是set，未知，丢弃
                return;
            }

            // 客户端发来的一个关闭会话请求
            Session session = conn.getOwner();
            sessionManager.removeSession(session.getUsername());

            SessionClosePacket retSessionPacket = new SessionClosePacket(Packet.TYPE_RESULT);
            retSessionPacket.setFromAddress(packet.getToAddress());
            retSessionPacket.setToAddress(packet.getFromAddress());
            retSessionPacket.setExtra("ok".getBytes());

            session.process(retSessionPacket);

            session.close();
        }
    }
}
